#pragma once

#include "mini_redis/Skiplist.h"
#include <cstdint>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>

namespace mini_redis {

// 普通字符串
// 存字符串值 + 过期时间
struct ValueRecord
{
    std::string value;

    int64_t expire_at_ms = -1;
};

// 哈希表
struct HashRecord
{
    std::unordered_map<std::string, std::string> fields;

    int64_t expire_at_ms = -1;
};

struct ZSetRecord
{
    // 小集合使用 vector；超过阈值转为 skiplist
    bool use_skiplist = false;

    // 当 use_skiplist=false 使用
    std::vector<std::pair<double, std::string>> items;

    // 当 use_skiplist=true 使用
    std::unique_ptr<Skiplist> sl;

    // 快速查询成员对应的分数
    std::unordered_map<std::string, double> member_to_score;

    int64_t expire_at_ms = -1;
};

using HashRecordVector = std::vector<std::pair<std::string, HashRecord>>;

using ValueRecordVector = std::vector<std::pair<std::string, ValueRecord>>;

class KeyValueStore
{
public:
    /*普通字符串的API*/
    // 设置字符串类型键值对
    bool set(const std::string& key, const std::string& value,
             std::optional<int64_t> ttl_ms = std::nullopt);

    // 设置字符串类型键值对并直接指定过期时间戳
    bool setWithExpireAtMs(const std::string& key,
                           const std::string& value, int64_t expire_at_ms);

    // 获取字符串数据
    std::optional<std::string> get(const std::string& key);

    // 批量删除字符串类型键
    int del(const std::vector<std::string>& keys);

    // 检查某个键是否存在于整个键值存储中
    bool exists(const std::string& key);

    // 为字符串类型的键设置过期时间（存活时间），或者移除已有的过期时间
    bool expire(const std::string& key, int64_t ttl_seconds);

    // 查询字符串类型键的剩余存活时间
    int64_t ttl(const std::string& key);

    // 得到map_的大小
    inline size_t size() const
    {
        return map_.size();
    }

    // 定期随机抽查一部分可能过期的键，删除已过期的键，并返回本次删除的数量。
    int expireScanStep(int max_steps);

    // 获取所有字符串类型键值对的快照备份
    ValueRecordVector snapshot() const;

    /**哈希表的API**/
    // 给哈希表类型的键设置字段和对应的值
    int hset(const std::string& key, const std::string& field,
             const std::string& value);

    // 获取哈希类型键中指定字段的值
    std::optional<std::string> hget(const std::string& key,
                                    const std::string& field);

    // 删除哈希类型键中指定的一个或多个字段
    int hdel(const std::string&              key,
             const std::vector<std::string>& fields);

    // 检查哈希类型键中是否存在指定字段
    bool hexists(const std::string& key, const std::string& field);

    // 获取哈希类型键中所有字段和值
    std::vector<std::string> hgetallFlat(const std::string& key);

    // 获取哈希类型键中字段的数量
    int hlen(const std::string& key);

    // 为哈希类型键设置或取消过期时间
    bool setHashExpireAtMs(const std::string& key, int64_t expire_at_ms);

    // 创建哈希类型数据的快照
    HashRecordVector snapshotHash() const;

    /**有序集合的API**/
    // 添加或更新成员及其分数
    int zadd(const std::string& key, double score,
             const std::string& member);

    // 删除指定的一个或多个成员
    int zrem(const std::string&              key,
             const std::vector<std::string>& members);

    // 按排名范围（从低分到高分）获取有序集合中的成员
    std::vector<std::string> zrange(const std::string& key, int64_t start,
                                    int64_t stop);

    // 获取有序集合中指定成员的分数
    std::optional<double> zscore(const std::string& key,
                                 const std::string& member);

    // 为有序集合（zset）设置或取消过期时间
    bool setZSetExpireAtMs(const std::string& key, int64_t expire_at_ms);

    struct ZSetFlat
    {
        std::string                                 key;
        std::vector<std::pair<double, std::string>> items;
        int64_t                                     expire_at_ms;
    };

    // 创建所有有序集合（zset）的快照
    std::vector<ZSetFlat> snapshotZSet() const;

    // 获取存储中所有类型键的列表
    std::vector<std::string> listKeys() const;

private:
    // 获取现在的时间
    static int64_t nowMs();

    // 判断当前普通字符串是否过期
    static bool isExpired(const ValueRecord& r, int64_t now_ms);
    // 清理过期的普通字符串
    void cleanupIfExpired(const std::string& key, int64_t now_ms);

    // 判断当前哈希是否过期
    static bool isExpired(const HashRecord& r, int64_t now_ms);
    // 清理过期的哈希
    void cleanupIfExpiredHash(const std::string& key, int64_t now_ms);

    // 判断当前有序集合是否过期
    static bool isExpired(const ZSetRecord& r, int64_t now_ms);
    // 清理过期的有序集合
    void cleanupIfExpiredZSet(const std::string& key, int64_t now_ms);

private:
    // 过期键的全局索引表
    std::unordered_map<std::string, int64_t> expire_index_;

    // 普通字符串
    std::unordered_map<std::string, ValueRecord> map_;
    // 哈希
    std::unordered_map<std::string, HashRecord> hmap_;
    // 有序集合
    std::unordered_map<std::string, ZSetRecord> zmap_;

    // 锁
    mutable std::mutex mutex_;

    static constexpr size_t kZsetVectorThreshold = 128;
};

}    // namespace mini_redis
